# 1 "kvmvapic.S"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "kvmvapic.S"

# Local APIC acceleration for Windows XP and related guests

# Copyright 2011 Red Hat, Inc. and/or its affiliates

# Author: Avi Kivity <avi@redhat.com>

# This work is licensed under the terms of the GNU GPL, version 2, or (at your
# option) any later version. See the COPYING file in the top-level directory.


# 1 "optionrom.h" 1
# 23 "optionrom.h"
# 1 "../../include/hw/nvram/fw_cfg.h" 1
# 24 "optionrom.h" 2
# 38 "optionrom.h"
.macro read_fw VAR
 mov $\VAR, %ax
 mov $0x510, %dx
 outw %ax, (%dx)
 mov $0x511, %dx
 inb (%dx), %al
 shl $8, %eax
 inb (%dx), %al
 shl $8, %eax
 inb (%dx), %al
 shl $8, %eax
 inb (%dx), %al
 bswap %eax
.endm
# 13 "kvmvapic.S" 2

.code16; .text; .global _start; _start:; .short 0xaa55; .byte (_end - _start) / 512;

 # clear vapic area: firmware load using rep insb may cause
 # stale tpr/isr/irr data to corrupt the vapic area.
 push %es
 push %cs
 pop %es
 xor %ax, %ax
 mov $vapic_size/2, %cx
 lea vapic, %di
 cld
 rep stosw
 pop %es

 # announce presence to the hypervisor
 mov $vapic_base, %ax
 out %ax, $0x7e

 lret

 .code32
vapic_size = 2*4096

.macro fixup delta=-4
777:
 .text 1
 .long 777b + \delta - vapic_base
 .text 0
.endm

.macro reenable_vtpr
 out %al, $0x7e
.endm

.text 1
 fixup_start = .
.text 0

.align 16

vapic_base:
 .ascii "kvm aPiC"


 .long vapic_base ; fixup
 .long fixup_start ; fixup
 .long fixup_end ; fixup

 .long vapic ; fixup
 .long vapic_size
vcpu_shift:
 .long 0
real_tpr:
 .long 0
 .long up_set_tpr ; fixup
 .long up_set_tpr_eax ; fixup
 .long up_get_tpr_eax ; fixup
 .long up_get_tpr_ecx ; fixup
 .long up_get_tpr_edx ; fixup
 .long up_get_tpr_ebx ; fixup
 .long 0
 .long up_get_tpr_ebp ; fixup
 .long up_get_tpr_esi ; fixup
 .long up_get_tpr_edi ; fixup
 .long up_get_tpr_stack ; fixup
 .long mp_set_tpr ; fixup
 .long mp_set_tpr_eax ; fixup
 .long mp_get_tpr_eax ; fixup
 .long mp_get_tpr_ecx ; fixup
 .long mp_get_tpr_edx ; fixup
 .long mp_get_tpr_ebx ; fixup
 .long 0
 .long mp_get_tpr_ebp ; fixup
 .long mp_get_tpr_esi ; fixup
 .long mp_get_tpr_edi ; fixup
 .long mp_get_tpr_stack ; fixup

.macro kvm_hypercall
 .byte 0x0f, 0x01, 0xc1
.endm

kvm_hypercall_vapic_poll_irq = 1

pcr_cpu = 0x51

.align 64

mp_get_tpr_eax:
 pushf
 cli
 reenable_vtpr
 push %ecx

 fs/movzbl pcr_cpu, %eax

 mov vcpu_shift, %ecx ; fixup
 shl %cl, %eax
 testb $1, vapic+4(%eax) ; fixup delta=-5
 jz mp_get_tpr_bad
 movzbl vapic(%eax), %eax ; fixup

mp_get_tpr_out:
 pop %ecx
 popf
 ret

mp_get_tpr_bad:
 mov real_tpr, %eax ; fixup
 mov (%eax), %eax
 jmp mp_get_tpr_out

mp_get_tpr_ebx:
 mov %eax, %ebx
 call mp_get_tpr_eax
 xchg %eax, %ebx
 ret

mp_get_tpr_ecx:
 mov %eax, %ecx
 call mp_get_tpr_eax
 xchg %eax, %ecx
 ret

mp_get_tpr_edx:
 mov %eax, %edx
 call mp_get_tpr_eax
 xchg %eax, %edx
 ret

mp_get_tpr_esi:
 mov %eax, %esi
 call mp_get_tpr_eax
 xchg %eax, %esi
 ret

mp_get_tpr_edi:
 mov %eax, %edi
 call mp_get_tpr_edi
 xchg %eax, %edi
 ret

mp_get_tpr_ebp:
 mov %eax, %ebp
 call mp_get_tpr_eax
 xchg %eax, %ebp
 ret

mp_get_tpr_stack:
 call mp_get_tpr_eax
 xchg %eax, 4(%esp)
 ret

mp_set_tpr_eax:
 push %eax
 call mp_set_tpr
 ret

mp_set_tpr:
 pushf
 push %eax
 push %ecx
 push %edx
 push %ebx
 cli
 reenable_vtpr

mp_set_tpr_failed:
 fs/movzbl pcr_cpu, %edx

 mov vcpu_shift, %ecx ; fixup
 shl %cl, %edx

 testb $1, vapic+4(%edx) ; fixup delta=-5
 jz mp_set_tpr_bad

 mov vapic(%edx), %eax ; fixup

 mov %eax, %ebx
 mov 24(%esp), %bl



 lock cmpxchg %ebx, vapic(%edx) ; fixup
 jnz mp_set_tpr_failed


 cmp %bh, %bl
 jae mp_tpr_is_bigger
mp_isr_is_bigger:
 mov %bh, %bl
mp_tpr_is_bigger:

 rol $8, %ebx

 cmp %bh, %bl
 ja mp_set_tpr_poll_irq

mp_set_tpr_out:
 pop %ebx
 pop %edx
 pop %ecx
 pop %eax
 popf
 ret $4

mp_set_tpr_poll_irq:
 mov $kvm_hypercall_vapic_poll_irq, %eax
 kvm_hypercall
 jmp mp_set_tpr_out

mp_set_tpr_bad:
 mov 24(%esp), %ecx
 mov real_tpr, %eax ; fixup
 mov %ecx, (%eax)
 jmp mp_set_tpr_out

up_get_tpr_eax:
 reenable_vtpr
 movzbl vapic, %eax ; fixup
 ret

up_get_tpr_ebx:
 reenable_vtpr
 movzbl vapic, %ebx ; fixup
 ret

up_get_tpr_ecx:
 reenable_vtpr
 movzbl vapic, %ecx ; fixup
 ret

up_get_tpr_edx:
 reenable_vtpr
 movzbl vapic, %edx ; fixup
 ret

up_get_tpr_esi:
 reenable_vtpr
 movzbl vapic, %esi ; fixup
 ret

up_get_tpr_edi:
 reenable_vtpr
 movzbl vapic, %edi ; fixup
 ret

up_get_tpr_ebp:
 reenable_vtpr
 movzbl vapic, %ebp ; fixup
 ret

up_get_tpr_stack:
 reenable_vtpr
 movzbl vapic, %eax ; fixup
 xchg %eax, 4(%esp)
 ret

up_set_tpr_eax:
 push %eax
 call up_set_tpr
 ret

up_set_tpr:
 pushf
 push %eax
 push %ebx
 reenable_vtpr

up_set_tpr_failed:
 mov vapic, %eax ; fixup

 mov %eax, %ebx
 mov 16(%esp), %bl



 lock cmpxchg %ebx, vapic ; fixup
 jnz up_set_tpr_failed


 cmp %bh, %bl
 jae up_tpr_is_bigger
up_isr_is_bigger:
 mov %bh, %bl
up_tpr_is_bigger:

 rol $8, %ebx

 cmp %bh, %bl
 ja up_set_tpr_poll_irq

up_set_tpr_out:
 pop %ebx
 pop %eax
 popf
 ret $4

up_set_tpr_poll_irq:
 mov $kvm_hypercall_vapic_poll_irq, %eax
 kvm_hypercall
 jmp up_set_tpr_out

.text 1
 fixup_end = .
.text 0
# 328 "kvmvapic.S"
.text 2

.align 128

vapic:
. = . + vapic_size

.byte 0; .align 512, 0; _end:
